package com.db.williamchart.renderer;

import com.db.williamchart.ChartContract;
import com.db.williamchart.animation.ChartAnimation;
import com.db.williamchart.data.DonutDataPoint;
import com.db.williamchart.data.Frame;
import com.db.williamchart.data.configuration.DonutChartConfiguration;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class DonutChartRenderer implements ChartContract.DonutRenderer {
    private static final int FULL_DEGREES = 360;
    private static final Float IGNORE_STATE_POSITION = -1234f;

    private ChartContract.DonutView view;
    private ChartAnimation<DonutDataPoint> animation;

    private Frame innerFrameWithStroke = new Frame(0f, 0f, 0f, 0f);

    private List<DonutDataPoint> datapoints = new ArrayList<>();

    private DonutChartConfiguration chartConfiguration;

    public DonutChartRenderer(ChartContract.DonutView view,ChartAnimation<DonutDataPoint> animation){
        this.view = view;
        this.animation = animation;
    }
    @Override
    public Boolean preDraw(DonutChartConfiguration configuration) {
        chartConfiguration = configuration;

        if (chartConfiguration.getColorsSize() < datapoints.size()){
            throw new IllegalArgumentException("Number of datapoints is " + datapoints.size() +
                    " but only "+ chartConfiguration.getColorsSize()+" color(s) provided.");
        }

        float left = configuration.getPaddings().getLeft() + configuration.getThickness() / 2;
        float top = configuration.getPaddings().getTop() + configuration.getThickness() / 2;
        float right = configuration.getWidth() - configuration.getPaddings().getRight() - configuration.getThickness() / 2;
        float bottom = configuration.getHeight() - configuration.getPaddings().getBottom() - configuration.getThickness() / 2;

        innerFrameWithStroke = new Frame(left,top,right,bottom);

        List<DonutDataPoint> list = new ArrayList<>();

        for (DonutDataPoint donutDataPoint : datapoints){
            donutDataPoint.setScreenDegrees(donutDataPoint.getValue() * FULL_DEGREES / chartConfiguration.getTotal());
            list.add(donutDataPoint);

        }
        datapoints.clear();
        datapoints.addAll(list);

        datapoints.sort(Comparator.comparingInt(t -> t.getScreenDegrees().intValue()));

        animation.animateFrom(IGNORE_STATE_POSITION, datapoints, new ChartAnimation.ChartCallback() {
            @Override
            public void invoke(Float value) {
                view.postInvalidate();
            }
        });
        return true;
    }

    @Override
    public void draw() {
        if (chartConfiguration.getBarsBackgroundColor() != 0){
            view.drawBackground(innerFrameWithStroke);
        }
        List<Float> floats = new ArrayList<>();
        for (DonutDataPoint donutDataPoint : datapoints){
            floats.add(donutDataPoint.getScreenDegrees());
        }
        view.drawArc(floats,innerFrameWithStroke);
    }

    @Override
    public void render(List<Float> values) {
        List<Float> valueOffset = generateValuesOffset(values);
        int index = 0;
        datapoints = new ArrayList<>();
        for (Float f : values){
            datapoints.add(toDonutDataPoint(f,valueOffset.get(index)));

            index++;
        }

        view.postInvalidate();
    }

    @Override
    public void anim(List<Float> values, ChartAnimation<DonutDataPoint> animation) {
        List<Float> valueOffset = generateValuesOffset(values);
        int index = 0;
        datapoints = new ArrayList<>();
        for (Float f : values){
            datapoints.add(toDonutDataPoint(f,valueOffset.get(index)));

            index++;
        }
        this.animation = animation;
    }

    private List<Float> generateValuesOffset(List<Float> floats){
        List<Float> valueOffset = new ArrayList<>();
        int size = floats.size();
        for (int i = 0; i < size; i++) {
            float offset = i == 0 ? 0f : valueOffset.get(i - 1) + floats.get(i - 1);
            valueOffset.add(i,offset);
        }
        return valueOffset;
    }
    private DonutDataPoint toDonutDataPoint(Float t,Float offset){
        return new DonutDataPoint(t + offset);
    }
}
